package cn.itcast.rocketmq.spring;

import org.apache.rocketmq.client.producer.SendCallback;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * 描述：在RocketMQ中生产消息    "topic:tag"
 *
 * 总结：在实际使用场景中，利用何种发送方式，可以总结如下：
 * 当发送的消息不重要时，采用one-way方式，以提高吞吐量；
 * 当发送的消息很重要是，且对响应时间不敏感的时候采用sync方式;
 * 当发送的消息很重要，且对响应时间非常敏感的时候采用async方式；
 */

@Component
public class SpringProducer {

    // 注入rocketMQ的模板
    @Autowired
    private RocketMQTemplate rocketMQTemplate;

    /**
     * 发送消息
     *
     * @param topic
     * @param msg
     */
    public void sendMsg(String topic, String msg) {
        /*
            convertAndSend()没有返回结果，若是需要查看返回结果可以调用同步/异步发送方法  syncSend()/asyncSend()
         */
        this.rocketMQTemplate.convertAndSend(topic, msg);
    }

    /**
     * 发送消息采用同步模式，这种方式只有在消息完全发送完成之后才返回结果，此方式存在需要同步等待发送结果的时间代价。
     * 这种方式具有内部重试机制，即在主动声明本次消息发送失败之前，内部实现将重试一定次数，默认为2次（DefaultMQProducer＃getRetryTimesWhenSendFailed）。
     * 发送的结果存在同一个消息可能被多次发送给给broker，这里需要应用的开发者自己在消费端处理幂等性问题。
     */
    public void sync() {
        rocketMQTemplate.syncSend("topic-name:TAG", "send sync message !");
    }

    /**
     * 发送消息采用异步发送模式，消息发送后立刻返回，当消息完全完成发送后，会调用回调函数sendCallback来告知
     * 发送者本次发送是成功或者失败。
     * 异步模式通常用于响应时间敏感业务场景，即承受不了同步发送消息时等待返回的耗时代价。
     * 异步模式也在内部实现了重试机制，默认次数为2次（DefaultMQProducer#getRetryTimesWhenSendAsyncFailed}）。
     * 发送的结果同样存在同一个消息可能被多次发送给给broker，需要应用的开发者自己在消费端处理幂等性问题。
     */
    public void async() {
        rocketMQTemplate.asyncSend("topic-name", "send async message!", new SendCallback() {
            @Override
            public void onSuccess(SendResult sendResult) {
                System.out.println("send successful");
            }

            @Override
            public void onException(Throwable throwable) {
                System.out.println("send fail; " + throwable.getMessage());
            }
        });
    }

    /**
     * 采用one-way发送模式发送消息的时候，发送端发送完消息后会立即返回，不会等待来自broker的ack来告知本次消息发送是否完全完成发送。
     * 这种方式吞吐量很大，但是存在消息丢失的风险，所以其适用于不重要的消息发送，比如日志收集。
     */
    public void oneWay() {
        rocketMQTemplate.sendOneWay("topic-name", "send one-way message");
    }

}
